//
//  CodablePersistence.swift
//  FoundationCustom
//
//  Created by ZhouJiatao on 2018/11/21.
//  Copyright © 2018 Unknow. All rights reserved.
//

import Foundation

/**
 持久化模型。
 
 如果避免不同版本的模型不兼容？没关系，做好do catch。不兼容抛出异常，让调用者从网络获取，并覆盖旧的。
 
 */

public struct CodablePersistence {
    
    public init() {
        
    }
    
    public struct CodablePersistenceError: Error { }
    
    var mHomeDir: URL? {
        
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last?.appendingPathComponent("CodablePersistence")
        
    }
    
    func fullPath(withFileName fileName: String) -> URL? {
        return mHomeDir?.appendingPathComponent(fileName, isDirectory: false)
    }
    
    /// 持久化到路径为：默认目录/fileName
    public func save<T : Codable>(_ codableModel: T, as fileName: String, completeHandler: ((URL?,Error?)->Void)?) {
        
        guard let path = mHomeDir?.appendingPathComponent(fileName, isDirectory: false) else {
            
            completeHandler?(nil, NSError(domain:"CodablePersistence", code:0, userInfo:["description":"找不到路径"]))
            return
        }
        
        save(codableModel, toPath: path, completeHandler: completeHandler)
        
        
    }
    
    /// 持久化到指定路径
    public func save<T : Codable>(_ codableModel: T, toPath path: URL, completeHandler: ((URL?,Error?)->Void)?) {
        DispatchQueue.global(qos: .background).async {
            
            do {
                let data = try JSONEncoder().encode(codableModel)
                
                try self.makeFileExists(path: path)
                
                try data.write(to: path, options: .atomic)
                completeHandler?(path, nil)
                print("CodablePersistence: write data to \(path) success!!!")

            } catch {
                completeHandler?(nil, error)
                print(error)
            }
        
        }
    }
    
    
    private func makeFileExists(path: URL) throws {
        
        let fn = FileManager.default

        let directory = path.deletingLastPathComponent()
        if !fn.fileExists(atPath: directory.path) {
            do {
                try fn.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
                print("CodablePersistence: 创建文件夹成功 \(directory)")
            }catch {
                throw error
            }
        }
        
        if !fn.fileExists(atPath: path.path) {
            let success = fn.createFile(atPath: path.path, contents: nil, attributes: nil)
            if  success {
                print("CodablePersistence: 创建文件成功 \(path)")
            }
        }
    }
    
    /// 从缓存中获取模型
    ///
    /// - Parameters:
    ///   - fileName: 缓存名称
    ///   - type: 数据类型
    ///   - withinDays: 指定获取多少天以内的缓存。 等于0则表示不限制天数。
    public func retrive<T: Codable>(_ fileName: String, as type: T.Type, withinDays: Double,completeHandler: @escaping (T?,Error?)->Void ) {
        guard let path = mHomeDir?.appendingPathComponent(fileName, isDirectory: false) else {
            completeHandler(nil, NSError(domain:"CodablePersistence", code:0, userInfo:["description":"找不到路径"]))
            return
        }
        
        retrive(fromPath: path, as: type, withinDays: withinDays, completeHandler: completeHandler)
    }
    
    /// 从缓存中获取模型
    ///
    /// - Parameters:
    ///   - fileName: 缓存名称
    ///   - type: 数据类型
    ///   - withinDays: 指定获取多少天以内的缓存。 等于0则表示不限制天数。
    public func retrive<T: Codable>(fromPath path: URL, as type: T.Type, withinDays: Double,completeHandler: @escaping (T?,Error?)->Void ) {
        do {
            
            let attr = try FileManager.default.attributesOfItem(atPath: path.path)
            let modificationDate = attr[FileAttributeKey.modificationDate] as? Date
            let interval = abs(modificationDate?.timeIntervalSinceNow ?? Date().timeIntervalSince1970)
            let isWithinDays = withinDays == 0 ? true : interval < TimeInterval(withinDays * 24 * 60 * 60)
            
            guard isWithinDays else {
                completeHandler(nil,nil)
                return
            }
            
            let data = try Data(contentsOf: path)
            let model = try JSONDecoder().decode(type, from: data)
            debugLog(modelData: data, path: path)
            completeHandler(model,nil)
            
        } catch {
            completeHandler(nil, error)
            print(error)
        }
    }
    
    /// 清除缓存
    public func clear(completeHandler:@escaping (_ success: Bool)->Void ) {
        
        guard let dir = mHomeDir,
            let files = try? FileManager.default.contentsOfDirectory(atPath: dir.path) else {
            print("找不到缓存目录")
            completeHandler(false)
            return
        }
        
        guard files.count > 0 else {
            completeHandler(true)
            return
        }
        
        for file in files {
            do {
                
                try FileManager.default.removeItem(atPath: dir.appendingPathComponent(file).path)
                print("移除缓存文件: \(file)")
                completeHandler(true)
            } catch {
                print(error)
                completeHandler(false)
            }
        }
        
    }
    
    private func debugLog(modelData: Data, path: URL) {
        #if DEBUG
        print("=================从缓存获取==================")
        print("path:\n \(path)")
        
        print("json:\n \(String(data: modelData, encoding: .utf8) ?? "")")
        
        print("============================================")
        #endif
    }
    
}
